#include <nxos.h>
#include <PowerBox.h>

NX_PRIVATE char powerBoxCmdBuf[PWBOX_CMD_BUF_LEN];
NX_PRIVATE char *powerBoxEnvLine = NX_NULL;

NX_PRIVATE void PrintPrompt(void)
{
    char cwd[NX_FILE_MAX_PATH] = {0};
    PowerBoxGetCwd(cwd);
    NX_Printf("PowerBox %s> ", cwd);
}

NX_PRIVATE NX_Error ReadCmd(int termFd, char *cmd, int cmdlen)
{
    char ch;
    int cmdIdx = 0;

    while (1)
    {
        ch = '\0';
        /* read input key */
        if (NX_FileRead(termFd, &ch, 1) != 1)
        {
            continue;
        }
        
        if (!ch)
        {
            continue;
        }

        /* parse data */
        switch (ch)
        {
        case '\r':
        case '\n':
            cmd[cmdIdx] = '\0';
            NX_Printf("\n");
            if (cmd[0] != '\0')
            {
                return NX_EOK;
            }
            else
            {
                return NX_EAGAIN;
            }
            break;
        case '\b':
            if (cmdIdx > 0)
            {
                cmd[--cmdIdx] = '\0';
                NX_Printf("\b \b");
            }
            break;
        default:
            NX_Printf("%c", ch);
            cmd[cmdIdx++] = ch;
            break;
        }
    }
}

NX_PRIVATE NX_Error ExecuteCmd(char *cmdline)
{
    NX_Error err = NX_EOK;
    char appPath[NX_FILE_MAX_NAME] = {0};
    char *spacePos;
    char *cmd = cmdline;
    NX_U32 retCode = 0;

    while (*cmd == ' ') /* skip space in the head */
    {
        cmd++;
    }
    
    /* get app name */
    spacePos = NX_StrChr(cmd, ' ');
    if (spacePos == NX_NULL) /* no space, only app name */
    {
        NX_StrCopy(appPath, cmd);
    }
    else
    {
        NX_StrCopyN(appPath, cmd, spacePos - cmd + 1);
    }
#ifdef PWBOX_DEBUG
    NX_Printf("PowerBox: app name: `%s`\n", appPath);
#endif

    /* run build in cmd first */
    if (PowerBoxRunBuiltinCmd(appPath, cmd, powerBoxEnvLine) == NX_ENOSRCH)
    {
        NX_ProcessLaunch(appPath, NX_THREAD_CREATE_WAIT, &retCode, cmd, powerBoxEnvLine);
        err = NX_ErrorGet();
        if (err != NX_EOK)
        {
            NX_Printf("PowerBox: launch app `%s` failed with err %d/%s\n", appPath, err, NX_ErrorToString(err));
            return err;
        }
    }

#ifdef PWBOX_DEBUG
    NX_Printf("PowerBox: app `%s` return code %d\n", appPath, retCode);
#endif
    return err;
}

#define PWBOX_SCRIPT_BUF_SIZE 1024

NX_Error ExecuteScript(void)
{
    NX_Error err;
    int fd = NX_FileOpen(PWBOX_SCRIPT_PATH, NX_FILE_RDONLY, 0);
    if (fd < 0)
    {
        NX_Printf("no PowerBox script\n");
        return NX_EOK;
    }

    char fbuf[PWBOX_SCRIPT_BUF_SIZE];
    if (NX_FileRead(fd, fbuf, PWBOX_SCRIPT_BUF_SIZE) <= 0)
    {
        NX_Printf("read script failed!\n");
        return NX_ERROR;
    }

    NX_FileClose(fd);

    NX_Printf("PowerBox script:\n============\n%s\n============\n", fbuf);

    char cmdline[128];

    char *p = fbuf;
    while (*p)
    {
        NX_MemZero(cmdline, 128);
        char *q = cmdline;
        while (*p && *p != '\n' && *p != '\r')
        {
            *q++ = *p++;
        }
        /* skip new line */
        while (*p == '\n' || *p == '\r')
        {
            p++;
        }

        if (cmdline[0] && cmdline[0] != '#') /* execute cmd */
        {
            NX_Printf("PowerBox: do: %s\n", cmdline);
            if ((err = ExecuteCmd(cmdline)) != NX_EOK)
            {
                return err;
            }
        }
    }
    NX_Printf("execute PowerBox script done\n");
    return NX_EOK;
}

NX_Error NX_Main(char *cmdline, char *envline)
{
    NX_Printf("PowerBox go!\n");
    NX_Error err = NX_EOK;
    int fd = -1;
    
    powerBoxEnvLine = envline;

    fd = NX_FileOpen("/System/Device/console", NX_FILE_RDWR, 0);
    if (fd < 0)
    {
        NX_Printf("PowerBox: open vterm device failed!\n");
        return err;
    }

    /* execute PowerBox script */
    ExecuteScript();

    while (1)
    {
        PrintPrompt();
        // read cmd
        NX_MemZero(powerBoxCmdBuf, PWBOX_CMD_BUF_LEN);
        err = ReadCmd(fd, powerBoxCmdBuf, PWBOX_CMD_BUF_LEN);
        if (err != NX_EOK)
        {
            if (err == NX_EAGAIN)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        // exec cmd
        err = ExecuteCmd(powerBoxCmdBuf);
#ifdef PWBOX_0DEBUG
        NX_Printf("PowerBox: execute cmd: %s with state:%d/%s\n", powerBoxCmdBuf, err, NX_ErrorToString(err));
#endif
    }
    NX_Printf("PowerBox: abort with err %d/%s\n", err, NX_ErrorToString(err));
    NX_FileClose(fd);
    return NX_EOK;
}
